use std::ops::Not;
use std::sync::Mutex;
use std::thread;
use std::thread::spawn;
use std::time::{Duration, SystemTime};

use clipboard_rs::{
    Clipboard, ClipboardContext, ClipboardHandler, ClipboardWatcher, ClipboardWatcherContext,
};
use evdev_rs::{Device, InputEvent};
use evdev_rs::enums::{EV_KEY, EventCode, EventType};
use evdev_rs::ReadFlag;
use mouse_keyboard_input::key_codes::*;
use mouse_keyboard_input::VirtualDevice;

// 可以安装 evtest 软件来找出键盘是哪个设备：sudo pacman -S evtest
// 然后使用root权限运行evtest工具：sudo evtest
// 然后我发现/dev/event4这个设备是我的键盘设备。
// “/dev/input/event*”的事件编号与设备的联系不是固定的，它通常按系统检测到设备的先后顺序安排event文件的编号，
// 这对编写应用程序控制不太方便，我们可以通过“/dev/input/by-id”或“/dev/input/by-path”目录查看具体的硬件设备。
// 例如我通过 ll -h /dev/input/by-path 命令，知道/dev/input/by-path/pci-0000:3a:00.0-usbv2-0:1.2:1.0-event-kbd 是/dev/event4 的软链接，
// 也就是说文件 pci-0000:3a:00.0-usbv2-0:1.2:1.0-event-kbd 就是访问该键盘的事件设备，而且这个文件名与硬件的关系是固定的。
// const KEYBOARD_DEV: &str = "/dev/input/by-path/pci-0000:3a:00.0-usbv2-0:1.2:1.0-event-kbd"; // 替换为实际的设备文件路径
// const KEYBOARD_DEV: &str = "/dev/input/by-path/pci-0000:3a:00.0-usbv2-0:1.2:1.0-event-kbd"; // 替换为实际的设备文件路径

// !!!!!!! 注意：由于剪切板监听的功能依赖x11，所以在wayland会话中启动应用的时候需要指定DISPLAY环境变量，例如：`DISPLAY=:0 sudo ./quick-launcher`

struct ClipboardContent {
    system_time: SystemTime,
    content: String
}
static CLIPBOARD_CONTENT: Mutex<Option<ClipboardContent>> = Mutex::new(None);

struct Manager {
    ctx: ClipboardContext,
}

impl Manager {
    pub fn new() -> Self {
        let ctx = ClipboardContext::new().unwrap();
        Manager { ctx }
    }
}

impl ClipboardHandler for Manager {
    fn on_clipboard_change(&mut self) {
        let clipboard_text = self.ctx.get_text().unwrap();
        println!("剪切板发生变化，内容为：{}", &clipboard_text);

        if clipboard_text.is_empty().not() {
            // 锁住互斥量并获得数据访问权
            // 这里guard的作用域是临界区
            let mut guard = CLIPBOARD_CONTENT.lock().unwrap();
            guard.replace(ClipboardContent {
                system_time: SystemTime::now(),
                content: clipboard_text
            });
        }
    }
}

fn watch_clipboard(){
    let manager = Manager::new();

    let mut watcher = ClipboardWatcherContext::new().unwrap();

    watcher.add_handler(manager);
    watcher.start_watch();
}

fn launch_utools(virtual_device: &mut VirtualDevice){
    // 注：通过操作系统提供的【设置】功能先提前将Ctrl+Alt+U这个快捷键绑定到`utools`这个命令
    // 从而可以通过Ctrl+Alt+U启动utools。然而通过这种方式启动utools，utools不会自动读取剪切板中的内容并粘贴到搜索框，
    // 所以这里面在按下Ctrl+Alt+U之后等待一会儿，等utools界面出来后按下Ctrl+V，将将切板中的内容粘贴到搜索框

    // 按下快捷键Ctrl+Alt+U
    virtual_device.press(KEY_LEFTCTRL).unwrap();
    virtual_device.press(KEY_LEFTALT).unwrap();
    virtual_device.press(KEY_U).unwrap();

    // 松开Ctrl+Alt+U
    virtual_device.release(KEY_LEFTCTRL).unwrap();
    virtual_device.release(KEY_LEFTALT).unwrap();
    virtual_device.release(KEY_U).unwrap();

    // 如果最近10秒内剪切板中有新的内容，则将剪切板中的内容粘贴到utools中
    let mut guard = CLIPBOARD_CONTENT.lock().unwrap();
    if guard.is_none() {
        println!("剪切板内容为空");
        return;
    }
    let clipboard_content = guard.take();
    let duration_result = SystemTime::now().duration_since(clipboard_content.unwrap().system_time);
    let duration = duration_result.unwrap().as_secs();
    println!("剪切板中有内容，内容已经存在了{}秒", &duration);

    if duration <= 10 {
        // 等待utools启动
        thread::sleep(Duration::from_millis(1000));

        // 按下Ctrl+V
        virtual_device.press(KEY_LEFTCTRL).unwrap();
        virtual_device.press(KEY_V).unwrap();

        //松开Ctrl+V
        virtual_device.release(KEY_LEFTCTRL).unwrap();
        virtual_device.release(KEY_V).unwrap();
    }
}

fn launch_fsearch(virtual_device: &mut VirtualDevice){
    // 注：通过操作系统提供的【设置】功能先提前将Ctrl+Alt+F这个快捷键绑定到`fsearch`这个命令
    // 从而可以通过Ctrl+Alt+F启动FSearch。

    // 按下快捷键Ctrl+Alt+F
    virtual_device.press(KEY_LEFTCTRL).unwrap();
    virtual_device.press(KEY_LEFTALT).unwrap();
    virtual_device.press(KEY_F).unwrap();

    // 松开Ctrl+Alt+F
    virtual_device.release(KEY_LEFTCTRL).unwrap();
    virtual_device.release(KEY_LEFTALT).unwrap();
    virtual_device.release(KEY_F).unwrap();
}

fn find_keyboard_device()->String{
    let exe_path = std::env::current_exe().unwrap();
    let config_file_path = exe_path.parent().unwrap().join("keyboard.config").to_str().unwrap().to_string();
    let config = std::fs::read_to_string(config_file_path).unwrap();
    let lines: Vec<&str> = config.split('\n').collect();
    let mut iter = lines.iter().filter(|line| line.starts_with("keyboard-device="));
    iter.next().unwrap().split('=').skip(1).next().unwrap().to_string()
}

fn main() {
    let device_path = find_keyboard_device();
    let keyboard_device = Device::new_from_path(device_path).unwrap();

    spawn(watch_clipboard);

    let mut virtual_input_device = VirtualDevice::default().unwrap();

    let mut previous_event_opt: Option<InputEvent> = None;
    let mut double_clicked = false;
    loop {
        let ev = keyboard_device.next_event(ReadFlag::NORMAL).map(|val| val.1);
        match ev {
            Ok(ev) => {
                if let Some(event_type) = ev.event_type() {
                    if event_type == EventType::EV_KEY {
                        // ev.code用于判断是哪个键
                        // ev.value用于判断是按下还是释放
                        const PRESSED: i32 = 1;
                        const RELEASED: i32 = 0;
                        if ev.event_code == EventCode::EV_KEY(EV_KEY::KEY_LEFTALT) || ev.event_code == EventCode::EV_KEY(EV_KEY::KEY_RIGHTALT) {
                            if ev.value == PRESSED {
                                if let Some(ref previous_event) = previous_event_opt {
                                    let duration_in_milli_secs = (ev.time.tv_sec - previous_event.time.tv_sec) * 1000 + (ev.time.tv_usec - previous_event.time.tv_usec) / 1000;
                                    if previous_event.event_code == ev.event_code && duration_in_milli_secs < 500 {
                                        double_clicked = true;

                                        if ev.event_code == EventCode::EV_KEY(EV_KEY::KEY_LEFTALT) {
                                            println!("双击了左Alt键");
                                        }else {
                                            println!("双击了右Alt键");
                                        }
                                    }
                                }
                                previous_event_opt = Some(ev);
                            } else if ev.value == RELEASED {
                                if double_clicked {
                                    double_clicked = false;

                                    if ev.event_code == EventCode::EV_KEY(EV_KEY::KEY_LEFTALT) {
                                        launch_utools(&mut virtual_input_device);
                                    }else {
                                        launch_fsearch(&mut virtual_input_device);
                                    }

                                }
                            }
                        }
                    }
                }
            },
            Err(_e) => (),
        }
    }
}
